﻿/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

#ifndef COMPATIBLE_MSS_H
#define COMPATIBLE_MSS_H

#include "BurnToolCommon/Global.h"
#include "SecureLink/Channel.h"
#include "../platform/Securec/securec.h"

namespace MssLenght {
    const int MSS_CIPHER_CTX_IV_LENGTH = 16;
    const int MSS_CIPHER_CTX_GCM_IV_LENGTH = 12;
    const int MSS_CIPHER_CTX_GCM_TAG_LENGTH = 16;
    const int SOP_MAX_ALLOC_MEMORY_SIZE = 100 * 1024 * 1024;
    const uint32_t MUX_START_FLAG_NEW = 0xAF;
}

namespace BurnToolCommon {
struct MSP_MUX_PACKET_HEAD_STRU_CLASS {
public:
    MSP_MUX_PACKET_HEAD_STRU_CLASS()
        : auId(0),
          packetSn(0),
          channelId(0),
          type(0),
          secureFlag(0),
          dataSize(0),
          data(nullptr),
          dataSizeF8(0),
          dataF8(nullptr),
          startFlag(0),
          ver(0),
          dst(0),
          cmdId(0),
          crc16(0),
          buffer(nullptr),
          bufferSize(0),
          src(0),
          ctrl(0)
    {
    }
    virtual ~MSP_MUX_PACKET_HEAD_STRU_CLASS()
    {
        bufferSize = 0;
        if (buffer) {
            delete[] buffer;
            buffer = nullptr;
        }
        crc16 = 0;
        cmdId = 0;
        ver = 0;
        dst = 0;
        src = 0;
        ctrl = 0;
        startFlag = 0;
        if (data) {
            delete[] data;
            data = nullptr;
        }
        dataSize = 0;
        if (dataF8) {
            delete[] dataF8;
            dataF8 = nullptr;
        }
        dataSizeF8 = 0;
        type = 0;
        secureFlag = 0;
        channelId = 0;
        packetSn = 0;
        auId = 0;
    }
    virtual PBYTE GetBuffer()
    {
        return nullptr;
    }
    virtual uint32_t GetBufferSize()
    {
        return 0;
    }
    virtual void Init(PBYTE input, uint32_t inputLen)
    {
        Q_UNUSED(input);
        Q_UNUSED(inputLen);
    }
    virtual void GetDataType(BOOL &isCnf, BOOL &isSysCmd, BOOL &isRaeSpyCmd)
    {
        (type & 0x02) != 0 ? isCnf = true : isCnf = false;
        (type & 0x01) != 0 ? isSysCmd = true : isSysCmd = false;
        (type & 0x80) != 0 ? isRaeSpyCmd = true : isRaeSpyCmd = false;
    }
    virtual BOOL CheckCrc()
    {
        return FALSE;
    }
    void SetData(const PBYTE mData, uint32_t mDataSize)
    {
        if (mData == nullptr) {
            return;
        }
        if (this->data) {
            delete[] this->data;
            this->data = nullptr;
        }
        this->dataSize = mDataSize;
        if (this->dataSize > 0xFFFF) {
            return;
        }
        if (mDataSize > MssLenght::SOP_MAX_ALLOC_MEMORY_SIZE) {
            qDebug() << "malloc size is too large: " << dataSize;
            return;
        }

        if (mDataSize > 0) {
            this->data = new uint8_t[mDataSize];
        }
        if (this->data == nullptr) {
            return;
        }
        errno_t errMemcpy = memcpy_s(this->data, mDataSize, mData, mDataSize);
        if (errMemcpy != EOK) {
            return;
        }
    }

    uint32_t GetAuId() const
    {
        return auId;
    }
    uint32_t GetPacketSn() const
    {
        return packetSn;
    }
    uint32_t GetChannelId() const
    {
        return channelId;
    }
    uint32_t GetType() const
    {
        return type;
    }
    virtual uint32_t GetSecureFlag()
    {
        return secureFlag;
    }
    uint32_t GetPacketDataSize() const
    {
        return dataSize;
    }
    uint32_t GetStartFlag() const
    {
        return startFlag;
    }
    uint8_t* GetIvValue()
    {
        return iv;
    };
    uint8_t* GetGcmIvValue()
    {
        return gcmIv;
    }
    uint8_t* GetGcmTagValue()
    {
        return gcmTag;
    }
    uint32_t GetVer() const
    {
        return ver;
    }
    uint32_t GetCmdId() const
    {
        return cmdId;
    }
    PBYTE GetData()
    {
        return data;
    }
    uint32_t GetDst() const
    {
        return dst;
    }
    uint32_t GetSrc() const
    {
        return src;
    }
    uint32_t GetCtrl() const
    {
        return ctrl;
    }

    void SetDst(uint32_t mDst)
    {
        this->dst = mDst;
    }
    void SetSrc(uint32_t mSrc)
    {
        this->src = mSrc;
    }
    void SetCtrl(uint32_t mCtrl)
    {
        this->ctrl = mCtrl;
    }
    void SetAuId(uint32_t mAuId)
    {
        this->auId = mAuId;
    }
    void SetPacketSn(uint32_t mPacketSn)
    {
        this->packetSn = mPacketSn;
    }
    void SetChannelId(uint32_t mChannelId)
    {
        this->channelId = mChannelId;
    }
    void SetType(uint32_t mType)
    {
        this->type = mType;
    }
    virtual void SetSecureFlag(uint32_t flag)
    {
        this->secureFlag = flag;
    }
    void SetPacketDataSize(uint32_t mDataSize)
    {
        this->dataSize = mDataSize;
    }
    void SetStartFlag(uint32_t mStartFlag)
    {
        this->startFlag = mStartFlag;
    }
    void SetVer(uint32_t mver)
    {
        this->ver = mver;
    }
    void SetCmdId(uint32_t mCmdId)
    {
        this->cmdId = mCmdId;
    }

    uint8_t* SopMalloc(uint64_t size) const
    {
        if (size > MssLenght::SOP_MAX_ALLOC_MEMORY_SIZE) {
            qDebug() << "malloc size is too large: " << size;
            return nullptr;
        }

        if (size > 0) {
            return new uint8_t[size];
        }
        return nullptr;
    }

public:
    uint32_t auId; // 需要写入的NV ID
    uint32_t packetSn;
    uint32_t channelId;
    uint32_t type; // 需要写入的NV项数据的大小
    uint32_t secureFlag; // 31加密标志
    uint32_t dataSize;
    PBYTE data; // 数据缓冲区
    uint32_t dataSizeF8;
    PBYTE dataF8; // 数据缓冲区
    uint8_t iv[MssLenght::MSS_CIPHER_CTX_IV_LENGTH];

    uint32_t startFlag;
    uint32_t ver;
    uint32_t dst;
    uint32_t cmdId;
    uint32_t crc16;
    PBYTE buffer;
    uint32_t bufferSize;

    uint32_t src;
    uint32_t ctrl;
    uint8_t gcmIv[MssLenght::MSS_CIPHER_CTX_GCM_IV_LENGTH] = {0};
    uint8_t gcmTag[MssLenght::MSS_CIPHER_CTX_GCM_TAG_LENGTH] = {0};
};

class MSP_MUX_PACKET_HEAD_STRU_CLASS_0XF9 : public MSP_MUX_PACKET_HEAD_STRU_CLASS {
public:
    MSP_MUX_PACKET_HEAD_STRU_CLASS_0XF9()
    {
        ctrl = 0;
    }
    void SetSecureFlag(uint32_t flag) override
    {
        if (flag == 0) {
            this->ctrl = this->ctrl & 0xbf;
        } else {
            this->ctrl = this->ctrl | 0x40;
        }
    };
    uint32_t GetSecureFlag() override
    {
        return this->ctrl & 0x40;
    };
    PBYTE GetBuffer() override
    {
        if (buffer != nullptr) {
            delete[] buffer;
            buffer = nullptr;
        }
        if (dataSize > 0xffff) {
            return nullptr;
        }
        int front = 0;
        int after = 0;
        ctrl = 0x01 | ctrl;
        CtrlAnalyze(front, after, nullptr, 0, 0);
        int dataSizeTemp = dataSize + sizeof(MSP_MUX_PACKET_HEAD_STRU_0XF9) + front + after;
        if (dataSizeTemp > MssLenght::SOP_MAX_ALLOC_MEMORY_SIZE) {
            qDebug() << "malloc size is too large: " << dataSizeTemp;
            return nullptr;
        }

        if (dataSizeTemp > 0) {
            buffer = new uint8_t[dataSizeTemp];
        }
        if (buffer == nullptr) {
            return nullptr;
        }
        bufferSize = static_cast<uint32_t>(dataSize + sizeof(MSP_MUX_PACKET_HEAD_STRU_0XF9) + front + after);
        MSP_MUX_PACKET_HEAD_STRU_0XF9 *p = reinterpret_cast<MSP_MUX_PACKET_HEAD_STRU_0XF9 *>(buffer);
        p->startFlag = MssLenght::MUX_START_FLAG_NEW;
        p->packetDataSize = dataSize;
        p->ctrl = ctrl;
        SetCtrlFrontInfo(p->packetData, dataSize + front + after);
        errno_t errMemcpy = memcpy_s(p->packetData + front, dataSize, data, dataSize);
        if (errMemcpy != EOK) {
            return nullptr;
        }
        p->packetDataSize += front;
        crc16 = ChannelClass::Channel::SopCrc16(reinterpret_cast<uint8_t *>(p->packetData), p->packetDataSize);
        p->packetDataSize += after;
        errMemcpy = memcpy_s(p->packetData + dataSize + front, sizeof(uint16_t), &crc16, 2); // crc lenth 2
        if (errMemcpy != EOK) {
            return nullptr;
        }
        return buffer;
    };
    uint32_t GetBufferSize() override
    {
        if (dataSize > 0xffff) {
            return 0;
        }
        int count = 0;
        CtrlAnalyze(count);
        bufferSize = dataSize + sizeof(MSP_MUX_PACKET_HEAD_STRU_0XF9) + count;
        return bufferSize;
    };
    void Init(PBYTE input, uint32_t inputLen) override
    {
        MSP_MUX_PACKET_HEAD_STRU_0XF9 *p = reinterpret_cast<MSP_MUX_PACKET_HEAD_STRU_0XF9 *>(input);
        if (p == nullptr) {
            return;
        }
        startFlag = p->startFlag;
        if (startFlag != MssLenght::MUX_START_FLAG_NEW) {
            return;
        }

        dataSize = p->packetDataSize;
        ctrl = p->ctrl;
        int front = 0;
        int after = 0;
        if (p->packetDataSize + sizeof(MSP_MUX_PACKET_HEAD_STRU_0XF9) > inputLen) {
            return;
        }
        CtrlAnalyze(front, after, p->packetData, p->packetDataSize, inputLen);
        cmdId = 0;
        errno_t errMemcpy = memcpy_s(&cmdId, sizeof (cmdId), p->packetData + front, 2); // cmdid占2位
        if (errMemcpy != EOK) {
            return;
        }
        SetData(p->packetData + front, p->packetDataSize - front - after);
    };
    void GetDataType(BOOL &isCnf, BOOL &isSysCmd, BOOL &isRaeSpyCmd) override
    {
        Q_UNUSED(isCnf);
        Q_UNUSED(isRaeSpyCmd);
        Q_UNUSED(isSysCmd);
    };
    BOOL CheckCrc() override
    {
        if ((ctrl & 0x01) != 0) {
            return crc16 == crcF9;
        }
        return TRUE;
    };

private:
    void CtrlAnalyze(int &front, int &after, const PBYTE data, uint16_t packetDataSize, uint32_t inputLen)
    {
        front = 0;
        after = 0;
        if ((ctrl & 0x10) != 0) {
            after++;
        }
        if ((ctrl & 0x01) != 0) {
            after += 2; // 2 crc 占2位
            crc16 = 0;
            if (packetDataSize >= after) {
                crcF9 = ChannelClass::Channel::SopCrc16(reinterpret_cast<uint8_t *>(data), packetDataSize - after);
                errno_t errMemcpy = memcpy_s(&crc16, sizeof (uint32_t), data + packetDataSize - after, 2); // crc 占2位
                if (errMemcpy != EOK) {
                    return;
                }
            }
        }
        if ((ctrl & 0x02) != 0) {
            front++;
        }
        if ((ctrl & 0x0C) != 0) {
            front++;
        }
        if ((ctrl & 0x40) != 0) {
            if (packetDataSize + sizeof(MSP_MUX_PACKET_HEAD_STRU_0XF9) <= inputLen) {
                errno_t errMemcpy = memcpy_s(gcmIv, sizeof(gcmIv), data + front,
                                             MssLenght::MSS_CIPHER_CTX_GCM_IV_LENGTH);
                if (errMemcpy != EOK) {
                    return;
                }
                errMemcpy = memcpy_s(gcmTag, sizeof(gcmTag), data + sizeof(gcmIv) +
                                     front, MssLenght::MSS_CIPHER_CTX_GCM_TAG_LENGTH);
                if (errMemcpy != EOK) {
                    return;
                }
            }
            front += MssLenght::MSS_CIPHER_CTX_GCM_IV_LENGTH + MssLenght::MSS_CIPHER_CTX_GCM_TAG_LENGTH;
        }
    }

    void SetCtrlFrontInfo(const PBYTE data, uint32_t inputLen)
    {
        uint32_t front = 0;
        uint32_t after = 0;
        if ((ctrl & 0x01) != 0) {
            after += 2; // 2
        }
        if ((ctrl & 0x10) != 0) {
            after++;
        }
        if ((ctrl & 0x02) != 0) {
            if (front + 1 <= inputLen) {
                data[front] = 0x25;
            }
            front++;
        }
        if ((ctrl & 0x0C) != 0) {
            front++;
        }
        if ((ctrl & 0x40) != 0) {
            if (front + MssLenght::MSS_CIPHER_CTX_GCM_IV_LENGTH +
                    MssLenght::MSS_CIPHER_CTX_GCM_TAG_LENGTH <= inputLen) {
                errno_t errMemcpy = memcpy_s(data + front,
                                             MssLenght::MSS_CIPHER_CTX_GCM_IV_LENGTH, gcmIv, sizeof(gcmIv));
                if (errMemcpy != EOK) {
                    return;
                }
                errMemcpy = memcpy_s(data + sizeof(gcmIv) + front,
                                     MssLenght::MSS_CIPHER_CTX_GCM_TAG_LENGTH, gcmTag, sizeof(gcmTag));
                if (errMemcpy != EOK) {
                    return;
                }
            }
            front += MssLenght::MSS_CIPHER_CTX_GCM_IV_LENGTH + MssLenght::MSS_CIPHER_CTX_GCM_TAG_LENGTH;
        }
    }
    void CtrlAnalyze(int &count) const
    {
        count = 0;
        if ((ctrl & 0x01) != 0) {
            count += 2; // 2
        }
        if ((ctrl & 0x02) != 0) {
            count++;
        }
        if ((ctrl & 0x0C) != 0) {
            count++;
        }
        if ((ctrl & 0x10) != 0) {
            count++;
        }
        if ((ctrl & 0x40) != 0) {
            count += MssLenght::MSS_CIPHER_CTX_GCM_IV_LENGTH + MssLenght::MSS_CIPHER_CTX_GCM_TAG_LENGTH;
        }
    }
    using MSP_MUX_PACKET_HEAD_STRU_0XF9 = struct {
        uint8_t startFlag;
        uint8_t ctrl;
        uint16_t packetDataSize;
        uint8_t packetData[0];
    };
    uint32_t crcF9 = 0;
};


class MSP_MUX_PACKET_HEAD_STRU_FACTORY {
public:
    MSP_MUX_PACKET_HEAD_STRU_FACTORY()
        : type(Global::currentChipTypeId)
    {
    }
    ~MSP_MUX_PACKET_HEAD_STRU_FACTORY()
    {
    }
    MSP_MUX_PACKET_HEAD_STRU_CLASS *GetItem() const
    {
        switch (type) {
            case ChipType::SLEBS2X:
            case ChipType::SLE2982:
                return new MSP_MUX_PACKET_HEAD_STRU_CLASS_0XF9();
            default:
                return new MSP_MUX_PACKET_HEAD_STRU_CLASS_0XF9();
        }
    }
private:
    ChipType type;
};

struct MSP_DIAG_HEAD_REQ_STRU_CLASS {
public:
    MSP_DIAG_HEAD_REQ_STRU_CLASS()
        : sn(0),
          dataSize(0),
          data(nullptr),
          reqIdx(0),
          ctrl(0),
          cmdId(0),
          crc16(0),
          buffer(nullptr),
          bufferSize(0)
    {
    }
    virtual ~MSP_DIAG_HEAD_REQ_STRU_CLASS()
    {
        bufferSize = 0;
        if (buffer) {
            delete[] buffer;
            buffer = nullptr;
        }
        crc16 = 0;
        cmdId = 0;
        ctrl = 0;
        reqIdx = 0;
        if (data) {
            delete[] data;
            data = nullptr;
        }
        dataSize = 0;
        sn = 0;
    }

    virtual PBYTE GetBuffer()
    {
        return nullptr;
    }
    virtual uint32_t GetBufferSize()
    {
        return 0;
    }
    virtual void Init(PBYTE input, uint32_t inputLen)
    {
        Q_UNUSED(input);
        Q_UNUSED(inputLen);
    }
    virtual BOOL CheckCrc()
    {
        return FALSE;
    }
    void SetData(const PBYTE dataValue, uint32_t dataSizeValue)
    {
        if (dataValue == nullptr) {
            return;
        }
        if (this->data) {
            delete[] this->data;
            this->data = nullptr;
        }
        this->dataSize = dataSizeValue;
        if (this->dataSize > 0xFFFF) {
            return;
        }
        this->data = new BYTE[dataSizeValue];
        if (this->data == nullptr) {
            return;
        }
        errno_t errMemcpy = memcpy_s(this->data, dataSizeValue, dataValue, dataSizeValue);
        if (errMemcpy != EOK) {
            return;
        }
    }
    uint32_t GetSn() const
    {
        return sn;
    }
    uint32_t GetReqIdx() const
    {
        return reqIdx;
    }
    uint32_t GetCtrl() const
    {
        return ctrl;
    }
    uint32_t GetPacketDataSize() const
    {
        return dataSize;
    }
    uint32_t GetCmdId() const
    {
        return cmdId;
    }
    PBYTE GetData()
    {
        return data;
    }
    uint32_t GetCrc() const
    {
        return crc16;
    }
    void SetSn(uint32_t packetSn)
    {
        this->sn = packetSn;
    }
    void SetPacketDataSize(uint32_t dataSizeValue)
    {
        this->dataSize = dataSizeValue;
    }
    void SetCmdId(uint32_t cmdIdValue)
    {
        this->cmdId = cmdIdValue;
    }
    void SetReqIdx(uint32_t reqIdxValue)
    {
        this->reqIdx = reqIdxValue;
    }
    void SetCtrl(uint32_t ctrlValue)
    {
        this->ctrl = ctrlValue;
    }
public:
    uint32_t sn;
    uint32_t dataSize;
    PBYTE data;
    uint32_t reqIdx;
    uint32_t ctrl;
    uint32_t cmdId;
    uint32_t crc16;
    PBYTE buffer;
    uint32_t bufferSize;
};

class MSP_DIAG_HEAD_REQ_STRU_CLASS_0XF9 : public MSP_DIAG_HEAD_REQ_STRU_CLASS {
public:
    PBYTE GetBuffer() override
    {
        if (buffer) {
            delete[] buffer;
            buffer = nullptr;
        }
        if (dataSize == 0) {
            buffer = new BYTE[1 + sizeof(MSP_DIAG_HEAD_REQ_STRU_0XF9)]();
            MSP_DIAG_HEAD_REQ_STRU_0XF9 *p = reinterpret_cast<MSP_DIAG_HEAD_REQ_STRU_0XF9 *>(buffer);
            *reinterpret_cast<uint16_t *>(&p->serverId) = cmdId;
            return buffer;
        }
        if (dataSize > 0xffff) {
            return nullptr;
        }
        buffer = new BYTE[dataSize + sizeof(MSP_DIAG_HEAD_REQ_STRU_0XF9)]();
        if (buffer == nullptr) {
            return nullptr;
        }
        bufferSize = static_cast<uint32_t>(dataSize + sizeof(MSP_DIAG_HEAD_REQ_STRU_0XF9));
        MSP_DIAG_HEAD_REQ_STRU_0XF9 *p = reinterpret_cast<MSP_DIAG_HEAD_REQ_STRU_0XF9 *>(buffer);
        *reinterpret_cast<uint16_t *>(&p->serverId) = cmdId;
        errno_t errMemcpy = EOK;
        errMemcpy = memcpy_s(p->packetData, dataSize, data, dataSize);
        if (errMemcpy != EOK) {
            return nullptr;
        }
        return buffer;
    };
    uint32_t GetBufferSize() override
    {
        if (dataSize > 0xffff) {
            return 0;
        }
        bufferSize = static_cast<uint32_t>(dataSize + sizeof(MSP_DIAG_HEAD_REQ_STRU_0XF9));
        return bufferSize;
    };
    void Init(PBYTE input, uint32_t inputLen) override
    {
        if (inputLen < sizeof(MSP_DIAG_HEAD_REQ_STRU_0XF9)) {
            return;
        }
        MSP_DIAG_HEAD_REQ_STRU_0XF9 *p = reinterpret_cast<MSP_DIAG_HEAD_REQ_STRU_0XF9 *>(input);
        if (p == nullptr) {
            return;
        }
        cmdId = *reinterpret_cast<uint16_t *>(&p->serverId);
        SetData(p->packetData, inputLen - sizeof(uint16_t));
    };
    BOOL CheckCrc() override
    {
        return TRUE;
    };
private:
    using MSP_DIAG_HEAD_REQ_STRU_0XF9 = struct {
        uint8_t serverId;
        uint8_t cmdId;
        uint8_t packetData[0];
    };
};

class MSP_DIAG_HEAD_REQ_STRU_FACTORY {
public:
    MSP_DIAG_HEAD_REQ_STRU_FACTORY()
        : type(Global::currentChipTypeId)
    {
    }
    ~MSP_DIAG_HEAD_REQ_STRU_FACTORY()
    {
    }
    MSP_DIAG_HEAD_REQ_STRU_CLASS *GetItem() const
    {
        switch (type) {
            case ChipType::SLEBS2X:
            case ChipType::SLE2982:
                return new MSP_DIAG_HEAD_REQ_STRU_CLASS_0XF9();
            default:
                return new MSP_DIAG_HEAD_REQ_STRU_CLASS_0XF9();
        }
    }
private:
    ChipType type;
};
}

#endif // __SOP_H__
